package com.wink.admin.utils;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import com.wink.admin.entity.SysUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * json 工具类 JackJson
 *
 * @author xw
 * @date 2020-11-03 9:56
 */
public class JsonUtil {

    private final static Logger log = LoggerFactory.getLogger(JsonUtil.class);
    private static ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    private JsonUtil() {
    }

    static {
        // objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
        // objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        // objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);

        // // 设置时区
        // objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        // //所有的日期格式都统一为以下的格式，即为：yyyy-MM-dd HH:mm:ss
        // objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        // objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        //对象的所有字段全部例入
        OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        // 设置接受只有一个元素的数组的反序列化
        OBJECT_MAPPER.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
        // 设置时区
        OBJECT_MAPPER.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        // 设置时间格式
        OBJECT_MAPPER.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

        JavaTimeModule javaTimeModule = new JavaTimeModule();
        /** 序列化配置,针对java8 时间 **/
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));

        /** 反序列化配置,针对java8 时间 **/
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));

        /** 注册模块 **/
        OBJECT_MAPPER.registerModule(javaTimeModule)
                .registerModule(new Jdk8Module())
                .registerModule(new ParameterNamesModule()).configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }


    /**
     * 对象转json字符串
     *
     * @param obj 任意对象
     * @return java.lang.String
     * @author xw
     * @date 2020/11/3 10:24
     */
    public static <T> String toStr(T obj) {
        if (obj == null) {
            return null;
        }
        try {
            return obj instanceof String ? (String) obj : OBJECT_MAPPER.writeValueAsString(obj);
        } catch (Exception e) {
            log.warn("Parse object to String error", e);
            return null;
        }
    }

    /**
     * 返回格式化好的json字符串，返回有换行json串
     *
     * @param obj 任意对象
     * @return java.lang.String
     * @author xw
     * @date 2020/11/3 10:25
     */
    public static <T> String toFormatStr(T obj) {
        if (obj == null) {
            return null;
        }
        try {
            return obj instanceof String ? (String) obj : OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
        } catch (Exception e) {
            log.warn("Parse object to String error", e);
            return null;
        }
    }

    /**
     * 把JSON字符串转成对象,第一个T表示将此方法声明为泛型方法,Class<T> clazz表示反序列化为什么类型
     *
     * @param str   json字符串
     * @param clazz 类对象
     * @return T
     * @author xw
     * @date 2020/11/3 10:26
     */
    public static <T> T toObj(String str, Class<T> clazz) {
        if (StringUtils.isEmpty(str) || clazz == null) {
            return null;
        }
        try {
            return clazz.equals(String.class) ? (T) str : OBJECT_MAPPER.readValue(str, clazz);
        } catch (Exception e) {
            log.warn("Parse object to Object error", e);
            return null;
        }
    }

    /**
     * 实现String userListStr=JsonUtil.toObj(userList);这种的反序列化[重点]用于复杂的场景
     *
     * @param str           json字符串
     * @param typeReference 类型参考
     * @return T
     * @author xw
     * @date 2020/11/3 10:28
     */
    public static <T> T toObj(String str, TypeReference<T> typeReference) {
        if (StringUtils.isEmpty(str) || typeReference == null) {
            return null;
        }
        try {
            return (T) (typeReference.getType().equals(String.class) ? (T) str : OBJECT_MAPPER.readValue(str, typeReference));
        } catch (Exception e) {
            log.warn("Parse object to Object error", e);
            return null;
        }
    }

    /**
     * 这种的反序列化[重点]用于复杂的场景;?表示还没有确定返回值[重点]用于复杂的场景
     *
     * @param str             json字符串
     * @param collectionClass 类型参考
     * @param elementClass    元素类
     * @return T
     * @author xw
     * @date 2020/11/3 10:28
     */
    public static <T> T toObj(String str, Class<?> collectionClass, Class<?>... elementClass) {
        JavaType javaType = getCollectionType(OBJECT_MAPPER, collectionClass, elementClass);
        try {
            return OBJECT_MAPPER.readValue(str, javaType);
        } catch (Exception e) {
            log.warn("Parse object to Object error", e);
            return null;
        }
    }

    /**
     * map  转JavaBean
     */
    public static <T> T toObj(Map map, Class<T> clazz) {
        return OBJECT_MAPPER.convertValue(map, clazz);
    }

    /**
     * map  转JavaBean
     */
    public static <T> T toObj(Object obj, Class<T> clazz) {
        return OBJECT_MAPPER.convertValue(obj, clazz);
    }

    /**
     * json字符串转成map
     *
     * @param json
     * @return Map<String, Object>
     * @author xw
     * @date 2020/11/3 10:31
     */
    public static Map<String, Object> toMap(String json) {
        if (StringUtils.isEmpty(json)) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(json, new TypeReference<Map<String, Object>>() {
            });
        } catch (JsonParseException e) {
            log.error("json字符串转成map异常", e);
        } catch (JsonMappingException e) {
            log.error("json字符串转成map异常", e);
        } catch (IOException e) {
            log.error("json字符串转成map异常", e);
        }
        return null;
    }

    public static <K, V> Map<K, V> toMap(String json, TypeReference<Map<K, V>> typeReference) {
        if (StringUtils.isEmpty(json)) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(json, typeReference);
        } catch (JsonParseException e) {
            log.error("json字符串转成map异常", e);
        } catch (JsonMappingException e) {
            log.error("json字符串转成map异常", e);
        } catch (IOException e) {
            log.error("json字符串转成map异常", e);
        }
        return null;
    }

    public static LinkedHashMap<String, Object> toLinkedHashMap(String json) {
        if (StrUtil.isBlank(json)) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(json, new TypeReference<LinkedHashMap<String, Object>>() {
            });
        } catch (JsonParseException e) {
            log.error("json字符串转成LinkedHashMap异常", e);
        } catch (JsonMappingException e) {
            log.error("json字符串转成LinkedHashMap异常", e);
        } catch (IOException e) {
            log.error("json字符串转成LinkedHashMap异常", e);
        }
        return null;
    }


    /**
     * 深度转换json成map
     *
     * @param json
     * @return
     */
    public static Map<String, Object> toMapDeeply(String json) throws Exception {
        return json2MapRecursion(json, OBJECT_MAPPER);
    }

    /**
     * to json数组字符串转换为列表
     */
    public static <T> List<T> toList(String jsonArrayStr) throws Exception {
        List<T> list = (List<T>) OBJECT_MAPPER.readValue(jsonArrayStr, List.class);
        return list;
    }

    public static <T> List<T> toList(String jsonArrayStr, TypeReference<List<T>> typeReference) throws Exception {
        List<T> lst = OBJECT_MAPPER.readValue(jsonArrayStr, typeReference);
        return lst;
    }

    /**
     * to json数组字符串转换为列表
     */
    public static <T> List<T> toList(String jsonArrayStr, Class<T> cl) throws Exception {
        return OBJECT_MAPPER.readValue(jsonArrayStr, getCollectionType(OBJECT_MAPPER, ArrayList.class, cl));
    }


    /**
     * 把json解析成map，如果map内部的value存在jsonString，继续解析
     *
     * @param json
     * @param mapper
     * @return
     * @throws Exception
     */
    private static Map<String, Object> json2MapRecursion(String json, ObjectMapper mapper) throws Exception {
        if (json == null) {
            return null;
        }

        Map<String, Object> map = mapper.readValue(json, Map.class);

        for (Map.Entry<String, Object> entry : map.entrySet()) {
            Object obj = entry.getValue();
            if (obj != null && obj instanceof String) {
                String str = ((String) obj);

                if (str.startsWith("[")) {
                    List<?> list = json2ListRecursion(str, mapper);
                    map.put(entry.getKey(), list);
                } else if (str.startsWith("{")) {
                    Map<String, Object> mapRecursion = json2MapRecursion(str, mapper);
                    map.put(entry.getKey(), mapRecursion);
                }
            }
        }

        return map;
    }

    /**
     * 把json解析成list，如果list内部的元素存在jsonString，继续解析
     *
     * @param json
     * @param mapper 解析工具
     * @return
     * @throws Exception
     */
    private static List<Object> json2ListRecursion(String json, ObjectMapper mapper) throws Exception {
        if (json == null) {
            return null;
        }

        List<Object> list = mapper.readValue(json, List.class);

        for (Object obj : list) {
            if (obj != null && obj instanceof String) {
                String str = (String) obj;
                if (str.startsWith("[")) {
                    obj = json2ListRecursion(str, mapper);
                } else if (obj.toString().startsWith("{")) {
                    obj = json2MapRecursion(str, mapper);
                }
            }
        }

        return list;
    }

    /**
     * 获取泛型的Collection Type
     *
     * @param collectionClass 泛型的Collection
     * @param elementClasses  转换类
     * @return JavaType Java类型
     * @since 1.0
     */
    private static JavaType getCollectionType(ObjectMapper mapper, Class<?> collectionClass, Class<?>... elementClasses) {
        return mapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
    }

    //使用主函数进行测试
    public static void main(String[] args) {
        SysUser u1 = new SysUser();
        u1.setUserName("sadjask");
        u1.setPassword("sdasdas");
        u1.setId(0L);
        u1.setIsDeleted(0);
        u1.setCreatedDate(new Timestamp(new java.util.Date().getTime()));
        u1.setLastModifyDate(new Timestamp(new java.util.Date().getTime()));

        SysUser u2 = new SysUser();
        u2.setUserName("sadjask");
        u2.setPassword(null);
        u2.setId(0L);
        u2.setIsDeleted(0);
        u2.setCreatedDate(new Timestamp(new java.util.Date().getTime()));
        u2.setLastModifyDate(new Timestamp(new java.util.Date().getTime()));

        String user1Json = JsonUtil.toStr(u1);
        String user1JsonPretty = JsonUtil.toFormatStr(u1);
        log.info("user1Json:{}", user1Json);
        log.info("user1toFormatStr:{}", user1JsonPretty);
        log.info("********************");
        log.info("user1Json:{}", JsonUtil.toStr(u2));
        log.info("user1toFormatStr:{}", JsonUtil.toFormatStr(u2));

        SysUser sysUser = JsonUtil.toObj(user1Json, SysUser.class);
        log.info("user.toString():" + sysUser.toString());
        List<SysUser> sysUserList = new ArrayList<>();
        sysUserList.add(u1);
        sysUserList.add(u2);


        String userListStr = JsonUtil.toStr(sysUserList);
        log.info("userListStr:" + userListStr);
        log.info("/////////////////////////////");

        //这是重点的方法
        List<SysUser> sysUserListObj1 = JsonUtil.toObj(userListStr, new TypeReference<List<SysUser>>() {
        });
        //这是重点的方法
        List<SysUser> sysUserList2 = JsonUtil.toObj(userListStr, List.class, SysUser.class);

        log.info("userListObj1:" + sysUserListObj1);
        log.info("userList2:" + sysUserList2);
        log.info("/////////////////////////////");

        Map<String, Object> stringObjectMap = JsonUtil.toMap(user1Json);
        log.info("2map:" + stringObjectMap);
    }
}
